#!/usr/bin/python

import wx,os

from base import struct

import weakref

"""
From
	j knapka, WeakMethod, http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/81253
	
	Reference to a bound method that permits the associated object
	to be garbage collected.

	A normal bound method "hides" a strong reference to the bound
	method's object. That means that the object can't be
	garbage-collected until the bound method is disposed of...
	
	Normal weakref.refs to bound methods don't quite work the way one expects, 
	because bound methods are first-class objects; weakrefs to bound
	methods are dead-on-arrival unless some other strong reference
	to the same bound method exists.
"""

class WeakMethodBound :
	def __init__( self , f ) :
		self.f = f.im_func
		self.c = weakref.ref( f.im_self )
	def __call__( self , *arg ) :
		if self.c() == None :
			raise ReferenceError, 'Method called on dead object'
		apply( self.f , ( self.c() , ) + arg )

class WeakMethodFree :
	def __init__( self , f ) :
		self.f = weakref.ref( f )
	def __call__( self , *arg ) :
		if self.f() == None :
			raise ReferenceError, 'Function no longer exist'
		apply( self.f() , arg )

def WeakMethod( f ) :
	try :
		f.im_func
	except AttributeError :
		return WeakMethodFree( f )
	return WeakMethodBound( f )

"""
The event classes are adopted from 
	Zoran Isailovski, C#-Style Events in Python, http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/410686
"""

class EventSet:
	"""
	Two types of observer functions
	>>> def f(e):
	... 	print e
	
	Source of events
	>>> class Src:
	... 	def __init__(me):
	... 		me.events=EventSet('LinesAdded','LinesDeleted')
	... 	def fire1(me):
	... 		me.events.LinesAdded(src='me') # in real source would be 'src=me'
	... 	def fire2(me):
	... 		if me.events.LinesDeleted.ignored():
	... 			print 'no observers'
	... 			return
	... 		me.events.LinesDeleted(src='me')
	
	Test without optional argument
	>>> src=Src()
	>>> print src.events
	Events ['LinesDeleted', 'LinesAdded']
	>>> src.events.LinesAdded+=f
	>>> src.fire1()
	struct { name='LinesAdded', src='me' }
	
	Nothing for this yet, haven't registered any observers
	>>> src.fire2()
	no observers
	>>> src.events.LinesDeleted+=f
	>>> src.fire2()
	struct { name='LinesDeleted', src='me' }
	
	Check blocking
	>>> src.events.LinesDeleted.block()
	>>> src.fire2()
	>>> src.events.LinesDeleted.unblock()
	
	Removal of observers
	>>> src.events.LinesDeleted-=f
	>>> src.fire2()
	no observers
	>>> src.events.LinesDeleted-=f
	Traceback (most recent call last):
	ValueError: event LinesDeleted, remove observer -- Have no such observer.
	"""

	def __init__(me, *event_names):
		for s in event_names:
			me.add(s)
			
	def add(me,event_name):
		me.__dict__[event_name]=_EventSlot(event_name)
		
	def __getattr__(me,event_name):
		raise AttributeError('Event "%s" not declared\nOnly know events %s.'%(event_name,me.__dict__.keys()))
		
	def __str__(me):
		return 'Events %s'%me.__dict__.keys()
			
class _EventSlot:

	def __init__(me,name):
		me.targets=[]
		me.__name__=name
		me._blocked=False
		
	def block(me):   me._blocked=True
	def unblock(me): me._blocked=False
		
	def __str__(me):
		return 'event %s'%me.__name__
		
	def bind(me,f): me.__iadd__(f)
	
	def __iadd__(me,f): #?? get rid of this, replace with just .bind()
		me.targets.append(f)
		return me
	
	def __isub__(me,f):
		if f not in me.targets:
			raise ValueError("event %s, remove observer -- Have no such observer."%me.__name__)
		while f in me.targets:
			me.targets.remove(f)
		return me
	
	def broadcast(me,*args,**payload): #?? get rid of __call__
		me.__call__(*args,**payload)
		
	def __call__(me,*args,**kw):
		if me._blocked:
			return
		if len(args)<>0:
			assert len(args)==1,(args,kw)
			assert kw=={}
			event_payload=args[0]
		else:
			event_payload=struct(**kw)
			
		event_payload.__event__=me.__name__
		
		for f in me.targets:
			f(event_payload)
				
	def ignored(me):
		return me.targets==[]
		
def platform_eol():
	if wx.Platform == '__WXMSW__': return '\r\n'
	if wx.Platform == '__WXMAC__': return '\r'
	return '\n' # UNIX
	
def figure_out_eol(content,default_to_platform=True):
	"""
	>>> print figure_out_eol('')
	None
	>>> figure_out_eol('\\r')
	'\\r'
	>>> figure_out_eol('\\n')
	'\\n'
	>>> figure_out_eol('a\\r\\n')
	'\\r\\n'
	>>> figure_out_eol('\\r\\n\\r\\n\\n\\r')
	'\\r\\n'
	>>> figure_out_eol('\\n\\n\\r\\r\\n')
	'\\n'
	"""
	rn=0
	n=0
	r=0
	
	i=0
	while i < len(content):
		if content[i]=='\n':
			n += 1
		elif content[i]=='\r':
			if i<len(content)-1 and content[i+1]=='\n':
				rn += 1
				i += 1
			else:
				r += 1
		i += 1
				
	if max(rn,n,r)==0:
		if default_to_platform:
			return platform_eol()
		else:
			return None
		
	if rn > n:
		if rn > r:
			return '\r\n'
		else:
			return '\r'
	else:
		if n > r:
			return '\n'
		else:
			return '\r'

def samefile(f,g):
	if f is None or g is None: return False
	
	try:
		return os.path.samefile(f,g)
	except: # ?? not available on WIN, make sure an exception is thrown there
		return os.path.abspath(f)==os.path.abspath(g)
		
def Gray(b):
	return wx.Colour(b,b,b)
	
if __name__=="__main__":
	import doctest
	doctest.testmod()